﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System.Diagnostics;
using AbstraX.ServerInterfaces;
using System.Runtime.Serialization;
using MvvmTreeView;
using System.ServiceModel.DomainServices.Server;
using System.ComponentModel.DataAnnotations;
using AbstraX.XPathBuilder;
using AbstraX.Templates;
using AbstraX;


using AbstraX.TypeMappings;
using AbstraX.Contracts;

namespace EntityProvider.Web.Entities
{
    [DataContract, NodeImage("EntityProvider.Web.Images.NavigationProperty.png"), DebuggerDisplay("{ DebugInfo }"), ClientCodeGeneration(typeof(AbstraXClientInterfaceGenerator))]
    public class NavigationProperty : EntitiesBase, IEntityParent, IElement
    {
        private XmlNode node;
        private EntityType parent;
        private bool isCollectionType;
        private XmlNode childNode;
        private XmlNode foreignRoleNode;
        private string name;
        private string queryWhereProperty;
        private object queryWhereValue;
        private float childOrdinal;

        public NavigationProperty()
        {
        }

        [AbstraXExtensionAttribute("IEntityProviderExtension", "EntityProviderExtension")]
        public IAbstraXExtension LoadExtension()
        {
            return null;
        }

        public NavigationProperty(XmlDocument edmxDocument, XmlNamespaceManager namespaceManager, string name, XmlNode node, EntityType parentEntity)
        {
            this.EdmxDocument = edmxDocument;
            this.NamespaceManager = namespaceManager;
            this.node = node;
            this.parent = parentEntity;
            var regex = new Regex(@"(?<namespace>\w+?)\.(?<key>\w+)");
            var relationshipAttribute = node.Attributes["Relationship"];
            var match = regex.Match(relationshipAttribute.Value);
            var nameSpace = match.Groups["namespace"].Value;
            var relationship = match.Groups["key"].Value;
            var associationSetNode = this.EdmxDocument.SelectSingleNode("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/e:Schema[@Namespace='" + nameSpace + "']/e:EntityContainer/e:AssociationSet[@Name='" + relationship + "']", this.NamespaceManager);
            var association = associationSetNode.Attributes["Association"].Value;

            this.name = name;

            regex = new Regex(@"(?<namespace>\w+?)\.(?<key>\w+)");
            match = regex.Match(association);
            nameSpace = match.Groups["namespace"].Value;

            var key = match.Groups["key"].Value;
            var associationNode = this.EdmxDocument.SelectSingleNode("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/e:Schema[@Namespace='" + nameSpace + "']/e:Association[@Name='" + key + "']", this.NamespaceManager);
            
            this.foreignRoleNode = associationNode.SelectSingleNode("e:End[@Role!='" + parentEntity.Name + "']", this.NamespaceManager);
            var foreignEntityRole = foreignRoleNode.Attributes["Role"].Value;
            
            this.childNode = this.EdmxDocument.SelectSingleNode("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/e:Schema[@Namespace='" + nameSpace + "']/e:EntityType[@Name='" + foreignEntityRole + "']", this.NamespaceManager);

            childOrdinal = 2;
        }

        [Association("NavigationProperty_ChildEntities", "ID", "ParentID")]
        public List<EntityType> ChildEntities
        {
            get 
            {
                List<EntityType> childEntities = new List<EntityType>();

                if (childNode != null)
                {
                    var childName = childNode.Attributes["Name"].Value;
                    var multiplicity = foreignRoleNode.Attributes["Multiplicity"].Value;

                    childEntities.Add(new EntityType(this.EdmxDocument, this.NamespaceManager, childName, childNode, this));

                    if (multiplicity == "*")
                    {
                        isCollectionType = true;
                    }
                    else if (multiplicity != "1")
                    {
                        isCollectionType = false;
                    }
                }

                return childEntities;
            }
        }

        public IEnumerable<IElement> ChildElements
        {
            get 
            {
                var childElements = new List<IElement>();

                if (this.ChildEntities != null)
                {
                    foreach (var entity in this.ChildEntities)
                    {
                        childElements.Add(entity);
                    }
                }

                return childElements;
            }
        }

        [DataMember]
        public bool IsContainer
        {
            get
            {
                return true;
            }
        }

        [DataMember, Include, Association("Parent_BaseType", "ID", "ParentID")]
        public BaseType DataType
        {
            get
            {
                return null;
            }
        }

        public IEnumerable<IAttribute> Attributes
        {
            get 
            {
                return new List<IAttribute>(); 
            }
        }

        public IEnumerable<IOperation> Operations
        {
            get { return null; }
        }

        public void ExecuteWhere(string property, object value)
        {
            if (value is XPathAxisElement && (property == "ID" || property == "ParentID"))
            {
                var predicate = ((XPathAxisElement)value).Predicates.First();

                queryWhereProperty = predicate.Left;
                queryWhereValue = predicate.Right;
            }
            else
            {
                Debugger.Break();

                queryWhereProperty = property;
                queryWhereValue = value;
            }
        }

        public void ExecuteWhere(System.Linq.Expressions.Expression expression)
        {
            throw new NotImplementedException();
        }

        public void ExecuteWhere(AbstraX.XPathBuilder.XPathAxisElement element)
        {
            var predicate = element.Predicates.First();

            queryWhereProperty = predicate.Left;
            queryWhereValue = predicate.Right;
        }

        public void ClearPredicates()
        {
            queryWhereProperty = null;
            queryWhereValue = null;
        }

        public IRoot Root
        {
            get
            {
                IBase baseObject = this;

                while (baseObject != null)
                {
                    baseObject = baseObject.Parent;

                    if (baseObject is IRoot)
                    {
                        return (IRoot)baseObject;
                    }
                }

                return null;
            }
        }


        [DataMember]
        public string Name
        {
            get
            {
                return name;
            }
        }

        [DataMember]
        public string Description
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember]
        public string Documentation
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember]
        public float ChildOrdinal
        {
            get
            {
                return childOrdinal;
            }
        }

        [DataMember]
        public string DebugInfo
        {
            get
            {
                return name + ":" + ID;
            }
        }

        [DataMember]
        public IBase Parent
        {
            get
            {
                return parent;
            }
        }

        [DataMember]
        public string FolderKeyPair { get; set; }

        [DataMember]
        public DefinitionKind Kind
        {
            get
            {
                return DefinitionKind.Property;
            }
        }

        [DataMember]
        public bool HasChildren
        {
            get
            {
                return this.ChildEntities.Count > 0;
            }
        }

        [DataMember, Key]
        public string ID
        {
            get
            {
                return this.MakeID("Property='" + name + "'");
            }
        }

        [DataMember]
        public string ParentID
        {
            get
            {
                return this.Parent.ID;
            }
        }

        [DataMember]
        public string ImageURL
        {
            get
            {
                return string.Empty;
            }
        }

        [DataMember, Include, Association("Parent_Facet", "ID", "ParentID")]
        public Facet[] Facets
        {
            get
            {
                return null;
            }
        }

        [Exclude]
        public ContainerType AllowableContainerTypes
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ConstructType AllowableConstructTypes
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ContainerType DefaultContainerType
        {
            get { throw new NotImplementedException(); }
        }

        [Exclude]
        public ConstructType DefaultConstructType
        {
            get { throw new NotImplementedException(); }
        }
    }
}
